#pragma once
#include <iostream>
#include <mutex>
#include <condition_variable>


class Lock
{
    private:
    std::mutex mut_;
    public:
    void lock() {mut_.lock();}
    void unlock() {mut_.unlock();}
    std::mutex& get_mutex() {return mut_;} 
};

class RWLock
{
    private:
    Lock base_lock_;
    std::condition_variable read_cv_;
    std::condition_variable write_cv_;

    int active_reads_=0;
    bool active_write_=false;
    int waiting_writers_=0;

    public:
    void RLock(){
        std::unique_lock<std::mutex> lock(base_lock_.get_mutex());
        read_cv_.wait(lock,[this]{
            return !active_write_ && waiting_writers_==0;//没有写持有锁，也没有写在等待
        });

        active_reads_++;
    }

    void RUnLock(){
        std::unique_lock<std::mutex> lock(base_lock_.get_mutex());
        active_reads_--;
        if(active_reads_==0 && waiting_writers_>0)
        {
            write_cv_.notify_one();
        }
    }

    void WLock(){
        std::unique_lock<std::mutex> lock(base_lock_.get_mutex());
        waiting_writers_++;
        write_cv_.wait(lock,[this]{
            return !active_write_ && active_reads_==0;//唤醒的条件 
        });
        active_write_=true;
        waiting_writers_--;
    }
     void WUnLock(){
        std::unique_lock<std::mutex> lock(base_lock_.get_mutex());
        active_write_=false;
        if(waiting_writers_>0)
        {
            write_cv_.notify_one();
        }
        else{
            read_cv_.notify_all();//不用考虑有没有读在等待，没有副作用
        }
    }

    //RAII
    class ReadGuard{
        RWLock& rwlock_; //必须在初始化列表初始化
        explicit ReadGuard(RWLock & lock): rwlock_(lock) //禁止隐式类型转换
        {
            rwlock_.RLock();
        }
        ~ReadGuard()
        {
            rwlock_.RUnLock();
        }
        ReadGuard(const ReadGuard&)=delete;
        ReadGuard& operator=(const ReadGuard&)=delete;
    };
    class WriteGuard{
        RWLock& rwlock_; //必须在初始化列表初始化
        explicit WriteGuard(RWLock & lock): rwlock_(lock) //禁止隐式类型转换
        {
            rwlock_.WLock();
        }
        ~WriteGuard()
        {
            rwlock_.WUnLock();
        }
        WriteGuard(const ReadGuard&)=delete;
        WriteGuard& operator=(const ReadGuard&)=delete;
    };


};
